require 'ncursesw'

module Dephine

  # Public: Terminal User Interface for making a simple UI.
  # All methods are instance methods and should be called on an instance.
  #
  # Examples
  #
  #   t = TUI.new
  #   t.puts 'it uses ncurses'
  #   t.close
  #   # => 'it uses ncurses'
  class TUI
    # Public: Initialize a terminal user interface.
    def initialize
      Ncurses.initscr
      Ncurses.nonl
      Ncurses.cbreak
      Ncurses.noecho

      @screen = Ncurses.stdscr
      @screen.scrollok(true)
      @screen.keypad(true)

      @top = 0
      @contexts = []
      @callbacks = {}
    end

    def maxy
      @screen.getmaxy
    end

    # Public: Add a String to an Array named 'contexts'.
    # This method basically doesn't act like the built-in method 'puts',
    # but values of 'contexts' will be printed.
    #
    # text - The String that will be pushed to an Array.
    #
    # Returns the contexts Array.
    def puts(text = '')
      @contexts << text
    end

    # Public: Move up the scroll.
    #
    # Returns true or fase.
    def scroll_up
      if @top > 0
        @screen.scrl(-1)
        @top -= 1
        str = @contexts[@top]

        if str
          @screen.move(0, 0)
          @screen.addstr(str)
        end

        true

      else
        false
      end
    end

    # Public: Move down the scroll.
    #
    # Returns true or false.
    def scroll_down
      if @top + maxy < @contexts.length
        @screen.scrl(1)
        @top += 1
        str = @contexts[@top + maxy - 1]

        if str
          @screen.move(maxy - 1, 0)
          @screen.addstr(str)
        end

        true

      else
        false
      end
    end

    # Internal: Set some hot-keys for performing basic things.
    # Basic things are scroll up, scroll down and quit.
    #
    # Returns nothing.
    def hotkeys
      loop do
        c = @screen.getch

        case c
        when Ncurses::KEY_DOWN, 14, 'j'.ord
          scroll_down

        when Ncurses::KEY_UP, 16, 'k'.ord
          scroll_up

        when Ncurses::KEY_NPAGE
          (maxy - 2).times do
            break if !scroll_down
          end

        when Ncurses::KEY_PPAGE
          (maxy - 2).times do
            break if !scroll_up
          end

        when Ncurses::KEY_LEFT
          while scroll_up; end

        when Ncurses::KEY_RIGHT
          while scroll_down; end

        when 'q'.ord
          break

        when @callbacks[c]
          true

        end

        @screen.move(0, 0)
      end
    end

    # Public: Set a hot-key for calling a block.
    #
    # key   - The key to be set as a hot-key.
    # block - The block to be called when pressing the key.
    #
    # Examples
    #
    #   set_key 'q' do
    #     quit
    #   end
    #   # => nil
    #
    # Returns nothing.
    def set_key(key, &block)
      @callbacks[key.ord] = block
    end

    # Public: Close the created terminal user interface.
    # This method should be called for closing the terminal user interface.
    #
    # Returns nothing.
    def close
      @contexts[0..maxy-1].each_with_index do |line, idx|
        @screen.move(idx, 0)
        @screen.addstr(line.to_s)
      end

      @screen.move(0, 0)
      @screen.refresh
      hotkeys
      Ncurses.endwin
    end

    private :hotkeys

  end
end
